Mise à jour le 13/11/2021
Les différentes façons de construire une page HTML.

Les différentes façons de construire une page HTML.


Alors que le PHP est certainement le langage le plus populaire pour faire du web, la façon dont le contenu des pages est généré est, plus de 25 ans après sa création, encore très archaïque et disons-le franchement, pas très pro ! C'est pour dire : d'un côté on a les 23 design patterns écrits par 4 types il y a trois siècles dont les règles sont toujours aussi pertinentes, et de l'autre il y a l'espèce de chose visqueuse dont on se sert pour faire quelques balises HTML qui bave de tous les côtés. Cet article fait un peu le tour des façons les plus courantes de créer un contenu HTML à partir du PHP.

1. La méthode old school

Il est presque inutile de la présenter car tout développeur l'a rencontrée. Je veux bien sûr parler de cette façon-ci de faire : on dispose d'un fichier PHP et on passe notre temps à ouvrir et à fermer les balises <?php?> pour y intégrer notre code HTML.

De cette façon exactement :

<?php
$myArray = ['chaussure', 'chocolat'];
foreach ($myArray as $item) {
?>
    <li><?php echo $item;?></li>
<?php
}

Il va sans dire que si l'on commence en plus à y intégrer du code JS, cela devient vite un cauchemar à gérer (même si c'est à première vue malin de créer du code JS dynamique).

L'indentation est difficilement respectable (on mélange forcément l'indentation du PHP et l'indentation du HTML) ;
minimiser le contenu de la page HTML est compliqué : il faudrait récupérer ce que génère le PHP et le convertir, ce qui demande de configurer son serveur web pour le faire via des modules spécifiques alors que cela pourrait être fait en PHP directement ;
l'injection de code malveillant est plus probable avec ce genre de code car si l'on commence à injecter des valeurs provenant des utilisateurs (ex: la liste de vos plats préférés, plat que l'utilisateur peut librement écrire, TODO: écrire un article sur l'injection XSS), celui-ci pourra modifier le DOM de notre page sans trop de difficulté. Il faudrait éventuellement utiliser des fonctions de sécurité un peu partout, ce qui serait assez chronophage, inesthétique et source d'oubli ;
la duplication de code est facilitée. Dès lors, quid de l'ajout d'attributs HTML sur nos éléments ? Faut-il revenir sur tous nos fichiers PHP en faisant une recherche globale de chaque élément ?


2. La méthode via echo et autres

Je ne saurais vraiment dire si c'est pire que la version oldschool, peut-être que l'avantage est de toujours pouvoir encapsuler la génération du HTML dans des fonctions.

On ferait comme ceci :

<?php
$myArray = ['chaussure', 'chocolat'];
foreach ($myArray as $item) {
    echo sprintf('<li>%s</li>', $item);
}

Ici c'est toujours très mauvais, cependant on peut évacuer plusieurs problèmes induits par la solution oldchool, notamment celle de l'injection HTML par l'utilisateur. Il est en effet possible d'adapter notre code de cette façon-ci :

<?php
$myArray = ['chaussure', 'chocolat'];
foreach ($myArray as $item) {
    echo makeHTMLLine($item);
}

function makeHTMLLine(string $item): string
{
    return sprintf('<li>%s</li>', htmlentities($item));
}


On peut imaginer construire tout un tas de fonctions pour créer nos éléments HTML qui appelleraient une fonction d’échappement (ici il s'agit de htmlentities, il vaut mieux ne l'écrire qu'une seule et unique fois dans tout son code pour potentiellement protéger l'ensemble du site).

Au passage, il ne serait pas élégant d'intégrer le echo dans la fonction, mieux vaut lui aussi ne l'écrire qu'une et unique fois dans son code et ne l'appeler qu'à la fin de l’exécution.

3. Empile moi du HTML

Troisième solution donc, qui est d'une certaine façon une amélioration de la seconde : on construit le contenu de son HTML que l'on stocke dans une variable $htmlContent puis on l'affiche à la fin de l’exécution du programme.
Ce n'est certes toujours pas l'idéal mais on peut éventuellement imaginer que cette variable contient dans son initialisation l'ensemble du squelette de la page HTML que l'on souhaite construire (ie. toutes les balises de type meta, style, jusqu'au body).

4. Mon premier template HTML

On peut même imaginer que ce squelette est dans un fichier squelette.html, chargé via une méthode de type file_get_contents puis modifié à coup de str_replace.

4.1 Exemple d'un tel fichier squelette.html

<html>
    <body>{{body}}</body>
</html>

4.2 On ferait ensuite ce genre de code PHP :

<?php 
$htmlContent = file_get_contents('squelette.html');
$htmlContent = str_replace('{{body}}', '<div>mon contenu</div>', $htmlContent);
echo $htmlContent;

4.3 Pour avoir ce résultat en sortie de l’exécution :

<html>
    <body><div>mon contenu</div></body>
</html>

Les symboles moustaches moustaches {{}} peuvent bien sûr être remplacés par n'importe quoi d'autre, sous réserve que ce ne soient pas des caractères fréquents dans les mots syntaxiques du HTML. Vous pouvez essayer "<<>>" mais ce ne serait pas forcément judicieux, rien que pour la lisibilité.
En outre, on peut tout à fait imaginer que certains morceaux de squelette soient enregistrés dans des fichiers secondaires afin d'être réutilisés plus tard, par exemple le contenu du pied de page, le menu, l'entête, ou même des blocs indépendants (carrousel d'images, boutons etc...).

Rien qu'avec ces quelques bouts de code, on peut dire qu'on a conçu un moteur de template ! Avec ici une règle assez simple : remplacer le pattern {{body}} par du contenu dynamique.

Mais, cela se complique fortement dès que l'on souhaite ajouter des conditions sur notre contenu, faire des boucles ou encore de l'héritage entre les pages. Ecrire un tel moteur s'avère alors plus difficile. Mais certains l'ont fait.

5. Système de template avec la force de PHP

L'implémentation d'un moteur qui ne ferait que de l'injection de valeurs trouve vite ses limites. Il n'est en effet non possible de personnaliser son HTML en fonction des variables PHP (comme faire des listes, afficher un message différent en fonction d'une condition etc...).
Pour pallier à ceci, on peut créer un fichier de template qui ressemblerait à un template HTML mais qui serait en fait une fonction PHP.

On peut éventuellement s'en sortir de cette façon-ci via un Templater maison :

class Templater
{
    public static function render($tplName, $parameters)
    {
        $tplFilePath = __DIR__.'/template/'.$tplName.'.php';
        if (!is_file($tplFilePath)) {
            throw new \Exception('Template '.$tplFilePath.' is not found.');
        }

        $rendering = require('template/'.$tplName.'.php');

        return $rendering($parameters);
    }
}


Et créer un fichier de template comme 'page.php' par exemple :

<?php $page = function($p) { ?>
    <html>
        <head></head>
        <body><?php echo $p['name'];?></body>
    </html>
<?php } ?>


On l'appelerait ensuite de cette façon-ci :

require_once('Templater.php');

echo Templater::render('page', array(
'name' => 'dmeloni',
));


Ce n'est pas ce qu'il y a de plus classe mais l'avantage reste tout de même d'utiliser la puissance de PHP dans un template (donc de pouvoir profiter des switch (ça n'existe toujours pas en twig), du caractère triple égal (===), du foreach et du for classique (plutôt qu'un loop.index pour savoir où on est dans le tableau) et des constances de classes MyClass::CONSTANCE_NAME plutôt que d'utiliser la chose un peu barbare qui est const('App\\Domain\\...\\MyClass::CONSTANCE_NAME')).
Par contre, on perd la grande force de l'héritage des templates...donc dès que notre application dépasse une seule page HTML, la solution full PHP deviendra vite limitante (sauf à réécrire soi-même un moteur de template plus évolué...) afin d'éviter le copier coller des balises HTML communes entre les pages.

6. Moteurs de templates populaires

Pour n'en citer que deux, Twig et Smarty.
Alors autant le dire franchement, c'est certainement mieux que toutes les autres solutions dont on vient de parler, mais cela reste d'assez mauvaises solutions.

Car avec ce genre de moteur, on en revient à écrire des choses qui finalement sont extrêmement similaires avec notre solution old school.

Pour preuve, comparez ce qui suit.

6.1 Solution old school

<?php

$myArray = ['chaussure', 'chocolat'];
foreach ($myArray as $item) {
?>
    <li><?php echo $item;?></li>
<?php
}


6.2 Solution new school

En Twig 2 et 3

{% foreach item in myArray %} 
    <li>{{item}}</li>
{% endfor %}


En Smarty 3 (je ne sais plus pour la 2, surement que cela marche aussi)

{foreach $myArray as $item}
    <li>{$item}</li>
{/foreach}

Autrement dit, on fait exactement la même erreur avec ces moteurs de templates qu'avec du PHP de base : on mélange la logique et le squelette de la page, et ça c'est mal car l'intégration du HTML n'en sera que plus compliqué puisque l'on va mélanger des conditions, des boucles et pire parfois des instances de classes PHP que l'on injecterait dans le template comme ci de rien n'était avec de l'affichage de listes, de tableaux, d'images etc..
Donc quid dès lors que l'on revient sur la logique (ie, les règles métiers) ?: Il faut revenir sur tous nos templates et prendre le risque de faire du HTML invalide ou même des templates qui vont faire planter la page puisque telle ou telle variable renverra un null pointer exception ou quoique cela puisse être du même style, sérieusement une exception lancée au moment de la génération de la page ?! Est-ce vraiment sérieux ?

Donc, dans l'idéal, il faut séparer le template du code métier. Cela permettra dans le cadre d'une entreprise par exemple de permettre à l'intégrateur HTML de faire le design de ses pages sans se poser de question sur comment sont faits les objets manipulés par l'application.

Pour faire un autre parallèle qui ne divise quasiment plus les développeurs web : on ne mélange pas non plus le CSS et le HTML.

6.3 Les moteurs de templates bientôt populaires

Pour faire bref : ce sont ceux qui permettent d'injecter des valeurs dans notre template via des pointeurs au niveau du DOM (ex: Xpath ou règles CSS)

Exemple, voici le squelette de la page :

<html>
    <ul><li></li</ul>
</html>

Et une proposition de syntaxe :
newPopularTemplateEngine->do('repeat item in items to the li element', $items, $templateContent);

Contrairement aux moteurs classiques où l'on injecte la plupart du temps un tableau de variables que l'on réutilise dans le contenu du template, ici le squelette de la page doit pouvoir être ouvert via le navigateur, avec un contenu Lorem Ipsum pourquoi pas puisque celui-ci serait effacé par le moteur au moment de l'injection du contenu.

Le TAL est un langage qui est une réponse à cette problématique, il est implémenté dans plusieurs langages de programmation. Son approche est par contre un peu austère car les pointeurs sont écrits en XML avec des tags propres.
Le Haml est également une solution via des pointeurs proches du CSS.
Transphporm est surement pour l'heure la solution la plus porteuse d'espoir, il est fort probable que son nom apparaisse un de ces jours dans la liste du Awesome PHP

A noter que le moteur nommé Plates fait pale figure comparé à ces deux-là car il est très proche de la philosophie derrière les JSP.